
#include "RobboLito.h"
#include "RobboTripleBase.h"
#include <string.h>
#include "RobboTotalBase.h"

#define wEnumB wEnumBL
#define bEnumB bEnumBL

static const uint8 NX[4][2] = { {0, 0}, {2, 3}, {1, 3}, {1, 2} };

static int GetData (uint8* E, int ba, uint8* AB, uint8* A, int PR, int sb)
{
  /* GetData from 64-byte array E, or 16-byte if so */
  /* sb is starting WLD, AB is TripleBase->WLD_mapping and A the inverse */
  /* ba is the index we demand, PR is 64 or 16 as the limit */
  int n, b, i = 0, r = A[sb];
  uint8 ch, tr;
  while (ba >= 0 && i < PR) /* limit at 64 or when index decrements past 0 */
    {
      ch = E[i++];
      tr = (ch & 64) >> 6; /* caret for off result */
      n = ch & 31; /* run of size n unless larger */
      b = 0;
      if (ch & 32) /* flip size one */
        {
          if (!ba)
            return AB[NX[r][tr]];
          else
            ba--; /* size one */
        }
      else
        r = NX[r][tr]; /* update result */
      while (ch & 128) /* large run */
        {
          b++;
          ch = E[i++];
          n += ((ch & 127) << (7 * b - 2));
        }
      ba -= n; /* size n */
    }
  return AB[r];
}

static int RobboTriple_valu (RobboTripleBase* TRIPLE_BASE, int i) /* uint64? */
{
  int w = 0, l, u;
  if (TRIPLE_BASE->w[2] == 0)
    return (TRIPLE_BASE->w[1]);
  l = TRIPLE_BASE->bs;
  if (l == -1)
    w = 0;
  else /* find index in index heap, make use 64 bytes cache? */
    {
      w = (2 << l) - 1;
      while (l >= 0)
        {
          u = TRIPLE_BASE->ind[w] >> 2;
          w += ((i > u) - (i < u)) << l;
          l--;
        }
      while (i >= (TRIPLE_BASE->ind[w] >> 2))
        w++;
      w--;
    }
  return
    GetData ((TRIPLE_BASE->data) + (TRIPLE_BASE->prop_ind * w),
	      i - (TRIPLE_BASE->ind[w] >> 2), TRIPLE_BASE->w,
	      TRIPLE_BASE->wi, TRIPLE_BASE->prop_ind, TRIPLE_BASE->ind[w] & 3);
}

static unsigned char nMUNE[64];
static unsigned char alfMUNE[64];
static unsigned char nepedMUNE[64];
static unsigned char bipedMUNE[64];
static unsigned char BLOCKED_MUNE[64];
static char ENUMn[65] =
  { A1, A2, B1, B2, A3, A4, B3, B4, C1, C2, D1, D2, C3, C4, D3, D4,
    A8, A7, B8, B7, A6, A5, B6, B5, C8, C7, D8, D7, C6, C5, D6, D5,
    H1, H2, G1, G2, H3, H4, G3, G4, F1, F2, E1, E2, F3, F4, E3, E4,
    H8, H7, G8, G7, H6, H5, G6, G5, F8, F7, E8, E7, F6, F5, E6, E5, -1 };

static char ENUM_BI_PED[49] =
  { A2, B2, C2, D2, E2, F2, G2, H2, A3, B3, C3, D3, E3, F3, G3, H3,
    A4, B4, C4, D4, E4, F4, G4, H4, A5, B5, C5, D5, E5, F5, G5, H5,
    A6, B6, C6, D6, E6, F6, G6, H6, A7, B7, C7, D7, E7, F7, G7, H7, -1 };

static char ENUM_NE_PED[49] =
  { A7, B7, C7, D7, E7, F7, G7, H7, A6, B6, C6, D6, E6, F6, G6, H6,
    A5, B5, C5, D5, E5, F5, G5, H5, A4, B4, C4, D4, E4, F4, G4, H4,
    A3, B3, C3, D3, E3, F3, G3, H3, A2, B2, C2, D2, E2, F2, G2, H2, -1 };

static char ENUM_ALF[65] =
  { A1, A3, B2, C1, A5, B4, C3, D2, E1, A7, B6, C5, D4, E3, F2, G1,
    B8, C7, D6, E5, F4, G3, H2, D8, E7, F6, G5, H4, F8, G7, H6, H8,
    H1, H3, G2, F1, H5, G4, F3, E2, D1, H7, G6, F5, E4, D3, C2, B1,
    G8, F7, E6, D5, C4, B3, A2, E8, D7, C6, B5, A4, C8, B7, A6, A8, -1 };

static char ENUM_BLOCKED[41] =
  { A2, B2, C2, D2, E2, F2, G2, H2, A3, B3, C3, D3, E3, F3, G3, H3,
    A4, B4, C4, D4, E4, F4, G4, H4, A5, B5, C5, D5, E5, F5, G5, H5,
    A6, B6, C6, D6, E6, F6, G6, H6, -1 };

int TRIPLE_BASE_TABLE[16][16][16][16];

#define reQU(x) \
  { if (P[x] == wEnumP) Q[x] = bipedMUNE[Q[x]]; \
    else if (P[x] == bEnumP) Q[x] = nepedMUNE[Q[x]]; \
    else if (P[x] == BlockedPawn) Q[x] = BLOCKED_MUNE[Q[x]]; \
    else if (P[x] == wEnumB || P[x] == bEnumB) Q[x] = alfMUNE[Q[x]]; \
    else Q[x] = nMUNE[Q[x]]; }

static boolean TRIPLE_opera (type_PiSq *PISQ, int *tb, uint64 *ind)
{
  int v, i, x, K1, K2, P[4], Q[4];
  int rf;
  RobboTripleBase* TRIPLE_BASE;
  if (PISQ->wtm)
    {
      K1 = PISQ->wK;
      K2 = PISQ->bK;
    }
  else
    {
      K1 = PISQ->bK;
      K2 = PISQ->wK;
      for (i = 0; i < 4; i++)
	PISQ->pi[i] = col_scamb[PISQ->pi[i]];
    }
  v = TRIPLE_BASE_TABLE[PISQ->pi[0]][PISQ->pi[1]][PISQ->pi[2]][PISQ->pi[3]];
  if ((v % 65536) == -1)
    return FALSE;
  *tb = v % 65536;
  TRIPLE_BASE = TABLE_TRIPLE_BASES + (v % 65536);
  if (TRIPLE_BASE->w[2] == 0) /* una resulta */
      return TRUE;
  for (i = 0; i < 4; i++)
    {
      x = (v >> (16 + 2 * i)) & 3;
      Q[i] = PISQ->sq[x];
      P[i] = PISQ->pi[x];
    }
  if (PISQ->pedone)
    {
      int w = (2 * (FILE (K2) >= FE)) + (!PISQ->wtm);
      for (i = 0; i < 4; i++)
	{
	  LANCpez (w, Q[i]);
	  if (P[i] == BlockedPawn && !PISQ->wtm)
            Q[i] -= 8;
	}
      LANCpez (w, K1);
      LANCpez (w, K2);
      rf = RE_pedone[64 * K1 + K2];
    }
  else
    {
      uint8 w = oLANC[K1][K2];
      for (i = 0; i < 4; i++)
	LANCnor (w, Q[i]);
      LANCnor (w, K1);
      LANCnor (w, K2);
      rf = RE_normale[64 * K1 + K2];
    }
  if (rf == -1)
    return FALSE;
  for (i = 0; i < 4; i++)
    reQU (i);
  for (i = 0; i < 4; i++)
    if (!P[i])
      Q[i] = 0;
  if (P[0] == P[2] && P[2] && P[1] == P[2])
    {
      if (Q[1] < Q[2])
	SCAMB (Q[1], Q[2]);
      if (Q[0] < Q[1])
	SCAMB (Q[0], Q[1]);
      if (Q[1] < Q[2])
	SCAMB (Q[1], Q[2]);
      Q[0] = (Q[0] * (Q[0] - 1) * (Q[0] - 2)) / 6 +
	(Q[1] * (Q[1] + 1)) / 2 - Q[1] + Q[2];
      Q[1] = Q[2] = 0;
    }
  else if (P[0] == P[1] && P[1])
    {
      if (Q[0] < Q[1])
	SCAMB (Q[0], Q[1]);
      Q[0] = (Q[0] * (Q[0] + 1)) / 2 + Q[1] - Q[0];
      Q[1] = 0;
    }
  else if (P[1] == P[2] && P[2])
    {
      if (Q[1] < Q[2])
	SCAMB (Q[1], Q[2]);
      Q[1] = (Q[1] * (Q[1] + 1)) / 2 + Q[2] - Q[1];
      Q[2] = 0;
    }
  *ind = ((((rf * TRIPLE_BASE->m[0]) + Q[0]) * TRIPLE_BASE->m[1] + Q[1])
	  * TRIPLE_BASE->m[2] + Q[2]) * TRIPLE_BASE->m[3] + Q[3];
  return TRUE;
}

static boolean triple_opera (typePOS *POSITION, int *r)
{
  int tb;
  uint64 ind, U, V, W;
  type_PiSq PiSq[1];
  boolean b;
  PiSq->pi[0] = PiSq->pi[1] = PiSq->pi[2] = PiSq->pi[3] = 0;
  PiSq->sq[0] = PiSq->sq[1] = PiSq->sq[2] = PiSq->sq[3] = 0;
  U = wBitboardP & (bBitboardP >> 8);
  V = (U << 1) & U & ~FILEa;
  W = (V << 1) & V;
  PiSq->Blocked = (U & ~V) | (W & ~((W << 1) & W));
  b = BITBORDO_PQ (POSITION, PiSq);
  if (b)
    b = TRIPLE_opera (PiSq, &tb, &ind);
  if (b)
    {
      if (((TABLE_TRIPLE_BASES + tb)->w[2]) == 0)
	*r = 2 - (TABLE_TRIPLE_BASES + tb)->w[1];
      else
	*r = 2 - RobboTriple_valu ((TABLE_TRIPLE_BASES + tb), ind);
      POSITION->tbhits++;
      return TRUE;
    }
  while (PiSq->Blocked && PiSq->n < 4)
    {
      BitClear (0, PiSq->Blocked);
      b = BITBORDO_PQ (POSITION, PiSq);
      if (b)
	b = TRIPLE_opera (PiSq, &tb, &ind);
      if (b)
	{
	  if (((TABLE_TRIPLE_BASES + tb)->w[2]) == 0)
	    *r = 2 - (TABLE_TRIPLE_BASES + tb)->w[1];
	  else
	    *r = 2 - RobboTriple_valu ((TABLE_TRIPLE_BASES + tb), ind);
	  POSITION->tbhits++;
	  return TRUE;
	}      
    }
  return FALSE;
}

#define vaVIT 1
#define vaPAT 0
#define vaPER -1

#define IN_CHECK \
  (POSITION->wtm ? \
   (wBitboardK & POSITION->DYN->bAtt) : (bBitboardK & POSITION->DYN->wAtt) )
#define IS_ILLEGAL \
  (!POSITION->wtm ? \
   (wBitboardK & POSITION->DYN->bAtt) : (bBitboardK & POSITION->DYN->wAtt) )

boolean TRIPLE_VALUE (typePOS* POSITION, int* Value)
{
  int r = vaPER, b = vaPER, i, move;
  typeMoveList LIST[256], *list;
  uint64 u;
  if (POSITION->DYN->oo || POSITION->DYN->ep) return FALSE;
  if (IS_ILLEGAL)
    return FALSE;
#ifdef ROBBO_SCACCO
  if (POPCNT (POSITION->OccupiedBW) >= 5 && (u = IN_CHECK))
    {
      list = EvasionMoves (POSITION, LIST, 0xffffffffffffffff);
      if (list == LIST)
	{
	  *Value = vaPER;
	  return TRUE;
	}
      for (i = 0; i < list - LIST; i++)
	{
	  move = LIST[i].move & 0x7fff;
	  Make (POSITION, move);
	  Mobility (POSITION);
	  if (IS_ILLEGAL)
	    {
	      Undo (POSITION, move);
	      continue;
	    }
	  if (!TRIPLE_VALUE (POSITION, &r))
	    {
	      Undo (POSITION, move);
	      return FALSE;
	    }
	  r = -r;
	  Undo (POSITION ,move);
	  if (r == vaVIT)
	    {
	      *Value = vaVIT;
	      return TRUE;
	    }
	  if (r == vaPAT)
	    b = vaPAT;
	}
      *Value = b;
      return TRUE;
    }
#endif
  list = CaptureMoves (POSITION, LIST, 0xffffffffffffffff);
  for (i = 0; i < list - LIST; i++)
    {
      move = LIST[i].move & 0x7fff;
      Make (POSITION, move);
      Mobility (POSITION);
      if (!IS_ILLEGAL)
	{
	  if (!TRIPLE_VALUE (POSITION, &r))
	    {
	      Undo (POSITION, move);
	      return FALSE;
	    }
	  r = -r;
	}
      Undo (POSITION, move);
      if (r == vaVIT)
	{
	  *Value = r;
	  return TRUE;
	}
      if (r == vaPAT)
	b = vaPAT;
    }
  if (!triple_opera (POSITION, &r))
    return FALSE;
  if (b > r)
    *Value = b;
  else
    *Value = r;
  return TRUE;
}

void READY_TRIPLE_BASE ()
{
  int i;
  for (i = A1; i <= H8; i++)
    nMUNE[(int) ENUMn[i]] = i;
  for (i = A1; i <= H8; i++)
    alfMUNE[(int) ENUM_ALF[i]] = i;
  for (i = 0; i < 48; i++)
    bipedMUNE[(int) ENUM_BI_PED[i]] = i;
  for (i = 0; i < 48; i++)
    nepedMUNE[(int) ENUM_NE_PED[i]] = i;
  for (i = 0; i < 40; i++)
    BLOCKED_MUNE[(int) ENUM_BLOCKED[i]] = i;
  INIT_TRIPLE_BASE ();
}
